# -*- coding: utf-8 -*-
"""
Created on Fri Dec 07 23:12:35 2012

@author: Winand
"""

#from SimpleHTTPServer import SimpleHTTPRequestHandler, BaseHTTPServer
#import urlparse, Cookie
#from datetime import datetime, timedelta
#def sendHtmlResponse(self, text, headers = {}):
#    self.send_response(200)
#    self.send_header("Content-type", "text/html")
#    self.send_header("Content-length", len(text))
##    self.send_header("Referer", self.path)
#    for key in headers:
#        self.send_header(key, headers[key])
#    self.end_headers()
#    self.wfile.write(text)

from __future__ import with_statement
from datetime import datetime, timedelta
import hashlib, random, re #, urlparse
from webapi.general import CookieWrapper, localPath
from webapi import serialize, http
import sitehandlers
import settings as st
from webapi.log import debug, error
from webapi.server.remotedict import RemoteDict

class login(object):
    HTML_NO_MORE_SESSIONS="Too many active sessions"
    #forget expired sessions
    def remExpiredAuth(self):
        debug("Cleaning sessions")
        for k, v in self.authinfo.getAll('time').iteritems():
            if datetime.now() - v > timedelta(minutes=st.TIMEOUT_SESS):
                debug("---", k)
                del self.authinfo[k]
    
    def genSessionNumber(self):
        return hashlib.md5(str(datetime.now())+str(random.random())).hexdigest()
        
    def hasSessionExpired(self, session):
        return datetime.now()-self.authinfo[session,'time'] \
                                    > timedelta(minutes=st.TIMEOUT_SESS)
                                    
    def isSessionAuthenticated(self, session):
        if session in self.authinfo:
            return self.authinfo.get((session,'authenticated'), False)
    
    #Check if session is valid, remove it if expired
    def validateSession(self, session):
        if not self.isSessionAuthenticated(session): return False #no such a session
        elif self.hasSessionExpired(session): #expired
            del self.authinfo[session]
            return False
        else: return True #valid one
    
    #get session from cookie or create a new one
    def getSession(self, resp):
        cookie=CookieWrapper(resp.cookie)
        session=cookie.get_val(st.COOKIE_SESS)
        if session not in self.authinfo: #new session
            if len(self.authinfo) >= st.MAX_CONN:
                self.remExpiredAuth() #Remove expired sessions when limit achieved
                if len(self.authinfo) >= st.MAX_CONN:
                    return #Too many active sessions
            session = self.genSessionNumber()
            self.authinfo[session] = {'time': datetime.now()} #init session
            cookie[st.COOKIE_SESS] = session
            cookie[st.COOKIE_SESS]['path'] = "/" #w/o path browser won't accept cookie on redirect (302)
            resp.headers['Set-Cookie'] = cookie.output()
        return session
       
    def checkauth(self, resp):
        sess=self.getSession(resp)
        if not sess: #cannot get or create session
            http.htmlResponse(resp, st.BASE_HTML % self.HTML_NO_MORE_SESSIONS)
            return True
        if resp.path == st.URL_AUTH:
            if self.validateSession(sess): #already authenticated?
                debug('already authenticated. go home')
                http.redirResponse(resp, st.URL_HOME) #go home
            elif resp.post_query and set(('uid','passw'))<=set(resp.post_query): #user tries to auth
                user = resp.post_query['uid'].value
                passw = resp.post_query['passw'].value
                auth_result, auth_msg = self.authenticateUser(user, passw, sess)
                if auth_result:
                    http.redirResponse(resp, \
                                self.authinfo.pop((sess,'nextpage'), st.URL_HOME))
                else:
                    self.authinfo[sess,'autherr'] = auth_msg
                    http.redirResponse(resp, st.URL_AUTH) #wrong id
            else:
                http.templateResponse(resp, "tpl_auth.html", \
                            {'title': "Authentication | lancomm",
                            'page_name': "Authentication",
                            'page_desc': "Input your username and password to log in",
                            'lasterror': self.authinfo.pop((sess,'autherr'), ""),
                            'default_user': getattr(st, "DEFAULT_USER", ""),
#                             'testiter': [str(i+1) for i in range(6)],
#                             'howmany': 30,
                            })
            return True
        #check wrong or expired session
        if not self.isSessionAuthenticated(sess) or self.hasSessionExpired(sess):
            self.authinfo[sess,'nextpage']=resp.path
            http.redirResponse(resp, st.URL_AUTH)
            return True
        else:
            self.authinfo[sess,'time'] = datetime.now() #update session start time
            return False
    
    def loadAuthDat(self):
        authdat = serialize.load(localPath("authdat")) #existing users
        debug("Loaded", len(authdat), "users")
        return RemoteDict("authdat", d=authdat if authdat else {})
        
    def Handle_ALL(self, resp):
        #WARNING: allow all http requests
        if resp.secure == False: return False
        
        self.authinfo = RemoteDict("sessions") #current sessions
        #TODO: return RemoteDict if var exists or smth.
        if not RemoteDict.exists("authdat"):
            self.authdat = self.loadAuthDat()
        else: self.authdat = RemoteDict("authdat")
        with self.authinfo: #open connection
            return self.checkauth(resp)
    
    #TODO: replace session if the same user logged in
    def authenticateUser(self, user, passw, session):
        #TODO: remove duplictation
        auths = sum(self.authinfo.getAll('authenticated', False).itervalues())
        if auths >= st.MAX_USER:
            self.remExpiredAuth() #Try to remove expired authentications
            auths = sum(self.authinfo.getAll('authenticated', False).itervalues())
            if auths >= st.MAX_USER:
                return False, "Online users limit achieved"
        debug('Sessions:', len(self.authinfo), '/', st.MAX_CONN, ", users online:", auths, '/', st.MAX_USER)
        if self.isSessionAuthenticated(session):
            return False, "Already authenticated"
        with self.authdat:
            if user not in self.authdat or \
            hashlib.sha1(user+passw).hexdigest() != self.authdat[user]:
                return False, "Wrong password or username %s not found" % user
        self.authinfo[session,'authenticated'] = True
        self.authinfo[session,'user'] = user
        debug('User authenticated', user)
        return True, ''

class router():
    def Handle_ALL(self, resp):
        for pattern, handler_s in st.URLHANDLERS.items():
            if not re.match(pattern+'$', resp.path): continue
            handler = getattr(sitehandlers, handler_s, None)
            if handler: handler(resp.path, resp)
            else: error("Url handler not found", handler_s)
        return resp.get("responded", False)

class lastresort():
    HTML_SERVER_ERROR="Server has not processed your request. <b>Contact administrator.</b>"
    def Handle_ALL(self, resp):
        error("Unhandled url", resp.path)
        http.htmlResponse(resp, st.BASE_HTML % self.HTML_SERVER_ERROR)
#        resp['file']="C:\\WinPython-32bit-2.7.3.1\\projects\\lancomm\\New Folder.zip"
#        resp['headers']['Content-Type']="application/octet-stream"

#import Cookie
#coo="csrftoken=b6d4a82a3907eabd04cab64448f0c103; sessionid=788f8c0d54237daa510cb8a847d2cbc7; MyCookie=12345"
#print Cookie.BaseCookie(coo).output(header='\0', sep='').strip('\0 ').split('\0 ')
